﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Netfraction.RemoteMachine
{
    public class PeerConnection : Connection
    {
        private Peer mPeer;
        private bool mIsConnectionOpen;

        public PeerConnection(Peer peer) : base(peer)
        {
            mPeer = peer;
            mIsConnectionOpen = false;
        }

        public bool IsConnectionOpen
        {
            get
            {
                return mIsConnectionOpen;
            }
        }

        protected override void OnConnect()
        {
            base.OnConnect();
        }

        protected override void OnMessageSend(Netfraction.Network.Protocol.ProtocolMessage message)
        {
            //throw new NotImplementedException();
        }

        protected override void OnDisconnect()
        {
            //throw new NotImplementedException();
        }

        protected override void OnMessageReceive(Netfraction.Network.Protocol.ProtocolMessage message)
        {
            System.Diagnostics.Debug.WriteLine(message.ToString());

            List<Network.Protocol.ProtocolFeature> featuresSupportingMessage = new List<Netfraction.Network.Protocol.ProtocolFeature>();
            List<Network.Protocol.ProtocolFeature> featuresSupportingMessageExtension = new List<Netfraction.Network.Protocol.ProtocolFeature>();
            foreach (Network.Protocol.ProtocolFeature f in mParentNode.Features.Values)
            {
                if (f.SupportsActionOfType(message.MessageCode, Netfraction.Network.Protocol.FeatureActionType.Message))
                    featuresSupportingMessage.Add(f);
                else if (f.SupportsActionOfType(message.MessageCode, Netfraction.Network.Protocol.FeatureActionType.MessageExtension))
                    featuresSupportingMessageExtension.Add(f);
            }

            // Call ProtocolFeature handlers
            foreach (Network.Protocol.ProtocolFeature feat in featuresSupportingMessage)
            {
                feat.HandleFeatureMessage(this.mParentNode, message, featuresSupportingMessageExtension);
            }
        }

        public void Listen(System.Net.IPAddress localIP, int portNumber)
        {
            mSocket = new System.Net.Sockets.Socket(localIP.AddressFamily, System.Net.Sockets.SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp);
            //IPAddress hostIP = (Dns.Resolve(IPAddress.Any.ToString())).AddressList[0];
            System.Net.IPEndPoint ep = new System.Net.IPEndPoint(localIP, portNumber);
            
            mSocket.Bind(ep);

            // start listening
            mSocket.Listen(5);
            BeginAsyncSocketAccept();
            //_socket.BeginAccept(base.BeginAsyncSocketConnect(_socket.RemoteEndPoint), _socket);
        }
        int fileOffset;
        public System.IO.FileStream fileStream;

        public long expectedBytes;

        protected override void OnDataReceive(byte[] buffer, int offset, int recv)
        {
            
            fileStream.Write(buffer, offset, recv);
            fileOffset += recv;
            //Console.WriteLine(fileOffset);
            if (fileOffset >= expectedBytes)
            {
                fileStream.Flush();
                string path = fileStream.Name;
                byte[] memBuffer = ReadFully(fileStream, (int)fileStream.Length);
                Managers.DownloadManager.DownloadCompleted(new System.IO.MemoryStream(memBuffer), this.mPeer);
                //fileStream.Close();
                this.State = ConnectionState.Normal;
                System.Diagnostics.Debug.WriteLine("file finished");
                fileStream.Close();
                fileStream = null;
                GC.Collect();
            }
        }

        /// <summary>
        /// Reads data from a stream until the end is reached. The
        /// data is returned as a byte array. An IOException is
        /// thrown if any of the underlying IO calls fail.
        /// </summary>
        /// <param name="stream">The stream to read data from</param>
        /// <param name="initialLength">The initial buffer length</param>
        public static byte[] ReadFully(System.IO.Stream stream, int initialLength)
        {
            // If we've been passed an unhelpful initial length, just use 32K.
            if (initialLength < 1)
                initialLength = 32768;

            byte[] buffer = new byte[initialLength];
            int read = 0;
            stream.Seek(0, System.IO.SeekOrigin.Begin);
            int chunk;
            while ((chunk = stream.Read(buffer, read, buffer.Length - read)) > 0)
            {
                read += chunk;

                // If we've reached the end of our buffer, check to see if there's any more information
                if (read == buffer.Length)
                {
                    int nextByte = stream.ReadByte();

                    // End of stream? If so, we're done
                    if (nextByte == -1)
                        return buffer;

                    // Nope. Resize the buffer, put in the byte we've just read, and continue
                    byte[] newBuffer = new byte[buffer.Length * 2];
                    Array.Copy(buffer, newBuffer, buffer.Length);
                    newBuffer[read] = (byte)nextByte;
                    buffer = newBuffer;
                    read++;
                }
            }
            // Buffer is now too big. Shrink it.
            byte[] ret = new byte[read];
            Array.Copy(buffer, ret, read);
            return ret;
        }
    }
}
